﻿using System.Net.DnsToolkit.Packet;
using System.IO;
using System.Net.DnsToolkit.Serializer;
using System.Threading;

namespace System.Net.DnsToolkit.Resolver {
    public class ServerResolver : IResolver {

        private readonly IDnsServerProvider _serverProvider;
        private readonly IQueryBuilder _queryBuilder;
        private readonly IDnsSocket _dnsSocket;
        private readonly IAnswerParser _answer;

        public ServerResolver(
                IDnsServerProvider serverProvider, 
                IQueryBuilder queryBuilder, 
                IDnsSocket dnsSocket,
                IMessageWriter<TextWriter> messageWriter,
                IAnswerParser answer    
            ) {

            _queryBuilder = queryBuilder;
            _serverProvider = serverProvider;
            _dnsSocket = dnsSocket;
            _answer = answer;

        }

        public bool TryResolv(string name, out IPAddress ipaddress) {
            return InternalResolv(true, name, out ipaddress);
        }

        public IPAddress Resolv(string name) {
            IPAddress ipaddress;
            InternalResolv(false, name, out ipaddress);
            return ipaddress;
        }

        private bool InternalResolv(bool tryResolv, string name, out IPAddress ipaddress) {

            bool? success = null;
            IMessage response = null;
            IPAddress[] servers = _serverProvider.Provide();
            
            var ev = new AutoResetEvent(false);
            var random = new Random();

            for (int i = 0; i < 5; i++) {

                IMessage query = _queryBuilder.Build(name);

                _dnsSocket.Send(
                    servers[random.Next(servers.Length)],
                    query,
                    e => {
                        success = e.Success;
                        response = e.Response;
                        ev.Set();
                    }
                );
            }

            ev.WaitOne(TimeSpan.FromSeconds(3));

            if (success == null) {
                ipaddress = null;
                return false;
            }

            ipaddress = ((bool)success) ? _answer.Parse(response) : null;
            return (bool)success;

        }
    }
}
